﻿using JWT.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

namespace BunnyEater.WebAPI.Attributes
{
    /// <summary>
    /// JWT授权过滤器，用于验证JWT令牌的有效性
    /// </summary>
    public class JwtAuthorizeFilter : Attribute, IAuthorizationFilter
    {
        /// <summary>
        /// 授权验证方法
        /// 判断是否允许匿名访问
        /// 401请求要求用户的身份认证
        /// </summary>
        /// <param name="context">授权过滤器上下文</param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            // 检查是否允许匿名访问
            if (IsEndpointAllowAnonymous(context))
            {
                return;
            }

            // 获取并验证令牌
            if (!TryGetToken(context.HttpContext.Request.Headers, out string token))
            {
                context.Result = new UnauthorizedResult();
                return;
            }

            try
            {
                if (IsTokenExpired(token))
                {
                    context.Result = new ForbidResult();
                    return;
                }
            }
            catch (SecurityTokenException ex) when (ex is SignatureVerificationException)
            {
                context.Result = new StatusCodeResult(StatusCodes.Status405MethodNotAllowed);
                return;
            }
            catch (Exception ex)
            {
                context.Result = new NotFoundResult();
                return;
            }
        }

        /// <summary>
        /// 检查端点是否允许匿名访问
        /// </summary>
        /// <param name="context">授权过滤器上下文</param>
        /// <returns>是否允许匿名访问</returns>
        private bool IsEndpointAllowAnonymous(AuthorizationFilterContext context)
        {
            var endpoint = context.HttpContext.GetEndpoint();
            return endpoint?.Metadata.GetMetadata<AllowAnonymousAttribute>() != null;
        }

        /// <summary>
        /// 尝试从请求头中获取令牌
        /// </summary>
        /// <param name="headers">请求头字典</param>
        /// <param name="token">输出的令牌</param>
        /// <returns>是否成功获取令牌</returns>
        private bool TryGetToken(IHeaderDictionary headers, out string token)
        {
            const string BearerPrefix = "Bearer ";

            if (headers.TryGetValue("Authorization", out var authHeader) ||
                headers.TryGetValue("auth", out authHeader))
            {
                token = authHeader.ToString();

                // 移除可能的"Bearer "或"auth "前缀
                if (token.StartsWith(BearerPrefix, StringComparison.OrdinalIgnoreCase))
                {
                    token = token.Substring(BearerPrefix.Length);
                }
                else if (token.StartsWith("auth ", StringComparison.OrdinalIgnoreCase))
                {
                    token = token.Substring(5);
                }

                return !string.IsNullOrEmpty(token);
            }

            token = null;
            return false;
        }

        /// <summary>
        /// 检查令牌是否已过期
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>令牌是否已过期</returns>
        private bool IsTokenExpired(string token)
        {
            var handler = new JwtSecurityTokenHandler();
            var jwtToken = handler.ReadJwtToken(token);

            if (jwtToken.Payload.Exp is not int expTimestamp)
            {
                return true; // 没有过期时间视为已过期
            }

            var expirationTime = DateTimeOffset.FromUnixTimeSeconds(expTimestamp).DateTime;
            return expirationTime <= DateTime.Now;
        }
    }
}
